package com.lsh.oms.core.service.order;

import com.lsh.base.lang.Exceptions;
import com.lsh.base.lang.Lists;
import com.lsh.base.lang.Maps;
import com.lsh.base.lang.Numbers;
import com.lsh.oms.core.dao.order.OrderDetailDao;
import com.lsh.oms.core.dao.order.OrderDetailExtDao;
import com.lsh.oms.core.dao.order.OrderHeadDao;
import com.lsh.oms.core.dao.order.OrderHeadExtDao;
import com.lsh.oms.core.dao.other.ReturnRequestMapper;
import com.lsh.oms.core.dao.payment.BillPaymentInfoDao;
import com.lsh.oms.core.enums.PayStatus;
import com.lsh.oms.core.exception.BusinessException;
import com.lsh.oms.core.model.order.*;
import com.lsh.oms.core.model.payment.BillPaymentInfo;
import com.lsh.oms.core.model.query.MisOrderHeadForPay;
import com.lsh.oms.core.records.core.*;
import com.lsh.oms.core.service.po.OmsTaskService;
import com.lsh.oms.core.service.shipping.core.ShippingService;
import com.lsh.oms.core.service.sign.core.SignService;
import com.lsh.oms.core.service.tool.Criteria;
import com.lsh.oms.core.service.tool.Pageable;
import com.lsh.oms.core.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.lsh.base.lang.BigDecimals.*;
import static com.lsh.base.lang.Lists.newArrayList;
import static com.lsh.base.lang.bean.Beans.getLong;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

/**
 * 订单查询service
 *
 * @author huangdong
 * @date 16/8/4
 */
@Service
public class OrderQueryService {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderQueryService.class);

    @Autowired
    private OrderHeadDao orderHeadDao;

    @Autowired
    private OrderHeadExtDao orderHeadExtDao;

    @Autowired
    private OrderDetailDao orderDetailDao;

    @Autowired
    private OrderDetailExtDao orderDetailExtDao;

    @Autowired
    private BillPaymentInfoDao billPaymentInfoDao;

    @Resource
    private ShippingService shippings;

    @Resource
    private SignService signs;

    @Resource
    private ReturnRequestMapper returnRequestMapper;

    @Resource
    private ReturnRequestRepository returnRequests;

    @Resource
    private ReturnRepository returns;

    @Resource
    private ReturnResultRepository returnResults;

    @Resource
    protected OmsTaskService tasks;

    @Value("${oms.host}")
    protected String omsHost;

    @Value("${oms.base.path}")
    protected String omsBasePath;

    /**
     * 查询订单信息
     *
     * @param filter      参数
     * @param fillExt     标记
     * @param fillDetails 标记
     * @return 订单列表
     */
    @Transactional(readOnly = true)
    public List<OrderHead> findList(OrderHead filter, boolean fillExt, boolean fillDetails) {
        List<OrderHead> entities = this.orderHeadDao.findList(filter);

        for (OrderHead orderHead : entities) {
            BillPaymentInfo billFilter = new BillPaymentInfo();
            billFilter.setPayStatus(PayStatus.PAID.getValue());
            if (null != orderHead.getParentOrderCode() && orderHead.getParentOrderCode() > 0) {
                billFilter.setBillCode(orderHead.getParentOrderCode());
            } else {
                billFilter.setBillCode(orderHead.getOrderCode());
            }

            List<MisOrderHeadForPay> misOrderHeadForPayList = this.billPaymentInfoDao.selectByFilter(billFilter);

            if (CollectionUtils.isEmpty(misOrderHeadForPayList)) {
                misOrderHeadForPayList = Collections.EMPTY_LIST;
            }

            orderHead.setPaySequences(misOrderHeadForPayList);
        }

        if (fillExt) {
            for (OrderHead entity : entities) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
        }
        if (fillDetails) {
            for (OrderHead entity : entities) {
                entity.setDetails(this.findDetails(entity.getOrderCode()));
            }
        }

        return entities;
    }

    @Transactional(readOnly = true)
    public List<OrderHead> findListByUid(Long uid, int fetchSize, boolean fillExt, boolean fillDetails) {
        List<OrderHead> entities = this.orderHeadDao.findListByUid(uid, fetchSize);

        for (OrderHead orderHead : entities) {
            BillPaymentInfo billFilter = new BillPaymentInfo();
            billFilter.setPayStatus(PayStatus.PAID.getValue());
            if (null != orderHead.getParentOrderCode() && orderHead.getParentOrderCode() > 0) {
                billFilter.setBillCode(orderHead.getParentOrderCode());
            } else {
                billFilter.setBillCode(orderHead.getOrderCode());
            }

            List<MisOrderHeadForPay> misOrderHeadForPayList = this.billPaymentInfoDao.selectByFilter(billFilter);

            if (CollectionUtils.isEmpty(misOrderHeadForPayList)) {
                misOrderHeadForPayList = Collections.EMPTY_LIST;
            }

            orderHead.setPaySequences(misOrderHeadForPayList);
        }

        if (fillExt) {
            for (OrderHead entity : entities) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
        }

        if (fillDetails) {
            for (OrderHead entity : entities) {
                entity.setDetails(this.findDetails(entity.getOrderCode()));
            }
        }

        return entities;
    }

    /**
     * 根据订单号获取订单信息
     *
     * @param orderCode   订单号
     * @param fillExt     标记
     * @param fillDetails 标记
     * @return 订单信息
     */
    @Transactional(readOnly = true)
    public OrderHead findByCode(Long orderCode, boolean fillExt, boolean fillDetails) {
        OrderHead filter = new OrderHead();
        filter.setOrderCode(orderCode);
        filter.setValid(1);
        OrderHead entity = this.orderHeadDao.findOne(filter);
        if (entity != null) {
            if (fillExt) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
            if (fillDetails) {
                entity.setDetails(this.findDetails(orderCode));
            }
        }
        return entity;
    }

    /**
     * 根据订单号获取订单信息
     *
     * @param orderCode   订单号
     * @param fillExt     标记
     * @param fillDetails 标记
     * @return OrderHead
     */
    @Transactional(readOnly = true, rollbackFor = RuntimeException.class)
    public OrderHead findByCodeAll(Long orderCode, boolean fillExt, boolean fillDetails) {
        OrderHead filter = new OrderHead();
        filter.setOrderCode(orderCode);
        OrderHead entity = this.orderHeadDao.findOne(filter);
        if (entity != null) {
            if (fillExt) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
            if (fillDetails) {
                entity.setDetails(this.findDetails(orderCode));
            }
        }
        return entity;
    }


    /**
     * 根据订单号获取订单信息
     *
     * @param misOrderCode 订单号
     * @param fillExt      标记
     * @param fillDetails  标记
     * @return 订单信息
     */
    @Transactional(readOnly = true, rollbackFor = RuntimeException.class)
    public OrderHead findByMisCode(String misOrderCode, boolean fillExt, boolean fillDetails) {
        OrderHead filter = new OrderHead();
        filter.setMisOrderNo(misOrderCode);
        filter.setValid(1);
        OrderHead entity = this.orderHeadDao.findOne(filter);
        if (entity != null) {
            if (fillExt) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
            if (fillDetails) {
                entity.setDetails(this.findDetails(entity.getOrderCode()));
            }
        }
        return entity;
    }

    /**
     * 根据订单号获取订单信息
     *
     * @param misOrderCode 订单号
     * @param fillExt      标记
     * @param fillDetails  标记
     * @return orderHead
     */
    @Transactional(readOnly = true)
    public OrderHead findByMisCodeValid(String misOrderCode, boolean fillExt, boolean fillDetails) {
        OrderHead filter = new OrderHead();
        filter.setMisOrderNo(misOrderCode);
        OrderHead entity = this.orderHeadDao.findOne(filter);
        if (entity != null) {
            if (fillExt) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
            if (fillDetails) {
                entity.setDetails(this.findDetails(entity.getOrderCode()));
            }
        }
        return entity;
    }

    /**
     * 根据订单号获取订单信息
     *
     * @param remark      订单号
     * @param fillDetails 标记
     * @return orderHead
     */
    @Transactional(readOnly = true)
    public OrderHead findByRemarks(String remark, boolean fillDetails) {
        OrderHeadExt filter = new OrderHeadExt();
        filter.setRemarks(remark);
        List<OrderHeadExt> entityList = this.orderHeadExtDao.findList(filter);
        if (CollectionUtils.isEmpty(entityList) || entityList.size() == 0) {
            return null;
        }
        OrderHeadExt orderHeadExt = entityList.get(0);

        OrderHead filter2 = new OrderHead();
        filter2.setId(orderHeadExt.getId());
        OrderHead entity = this.orderHeadDao.findOne(filter2);
        if (null == entity) {
            return null;
        }
        entity.setExtObject(orderHeadExt);
        if (fillDetails) {
            entity.setDetails(this.findDetails(entity.getOrderCode()));
        }
        return entity;
    }

    /**
     * 根据订单号获取订单信息
     *
     * @param parentOrderCode 父单号
     * @param fillExt         标记
     * @param fillDetails     标记
     * @return orderHead
     */
    @Transactional(readOnly = true)
    public List<OrderHead> findByParentOrderCode(Long parentOrderCode, boolean fillExt, boolean fillDetails) {
        OrderHead filter = new OrderHead();
        filter.setParentOrderCode(parentOrderCode);
        filter.setValid(1);
        List<OrderHead> entitys = this.orderHeadDao.findList(filter);
        if (CollectionUtils.isEmpty(entitys)) {
            return Collections.EMPTY_LIST;
        }
        for (OrderHead entity : entitys) {
            if (fillExt) {
                entity.setExtObject(this.orderHeadExtDao.get(entity.getId()));
            }
            if (fillDetails) {
                entity.setDetails(this.findDetails(entity.getOrderCode()));
            }
        }

        return entitys;
    }

    /**
     * 根据订单号查询订单明细列表
     *
     * @param orderCode 订单信息
     * @return orderCode
     */
    @Transactional(readOnly = true)
    public List<OrderDetail> findDetails(Long orderCode) {
        OrderDetail filter = new OrderDetail();
        filter.setOrderCode(orderCode);
        return this.orderDetailDao.findList(filter);
    }

    /**
     * 统计订单数
     *
     * @param filter 过滤对象
     * @return 个数
     */
    @Transactional(readOnly = true)
    public int count(OrderHead filter) {
        return this.orderHeadDao.count(filter);
    }


    /**
     * 根据地域和订单状态查询订单信息
     *
     * @param regionCode        区域
     * @param orderStatus       订单
     * @param excludeOrderCodes 信息
     * @param timeOffset        时间
     * @param fetchSize         个数
     * @param saleAreaLock      销售
     * @param storageType       类型
     * @return 订单列表
     */
    @Transactional(readOnly = true)
    public List<OrderHead> findListByRegionAndStatus(Integer regionCode, Integer orderStatus, Set<Long> excludeOrderCodes, int timeOffset, int fetchSize, String saleAreaLock, Integer storageType, String supplierGroupId) {
        return this.orderHeadDao.findListByRegionAndStatus(regionCode, orderStatus, excludeOrderCodes, timeOffset, fetchSize, saleAreaLock, storageType, supplierGroupId);
    }

    /**
     * 根据地址获取用户单号列表(按下单时间排序)
     *
     * @param addressCode 地址
     * @param limit       个数
     * @return 信息
     */
    @Transactional(readOnly = true)
    public List<Long> findOrderCodesByAddressCode(Long addressCode, int limit) {
        return this.orderHeadDao.findOrderCodesByAddressCode(addressCode, limit);
    }

    /**
     * 根据订单状态返回查询订单列表(id, order_code, order_status, order_time, create_time, update_time)
     *
     * @param start 开始
     * @param end   结束
     * @return 订单列表
     */
    @Transactional(readOnly = true)
    public List<OrderHead> findListByStatusRange(int start, int end) {

        Date pretime = DateUtil.getPreviousDayMorning(5);
        int preTimeint = (int) (pretime.getTime() / 1000);

        return this.orderHeadDao.findListByStatusRange(start, end, preTimeint);
    }

    @Transactional(readOnly = true)
    public List<OrderHead> findListByStatusRange(int start, int end, int preTime, int endtime) {

//        Date pretime = DateUtil.getPreviousDayMorning(5);
//        int preTimeint = (int) (pretime.getTime() / 1000);

        return this.orderHeadDao.findListByStatusRange2(start, end, preTime, endtime);
    }

    /**
     * 根据地址ID及订单状态区间查询订单
     *
     * @param addressCode 地址
     * @param start       开始
     * @param end         结束
     * @return 订单列表
     */
    @Transactional(readOnly = true)
    public List<OrderHead> findListByAddressCodeAndStatusRange(Long addressCode, int start, int end) {
        return this.orderHeadDao.findListByAddressCodeAndStatusRange(addressCode, start, end);
    }

    /**
     * 根据地址ID及订单状态区间及时间区间查询订单
     *
     * @param addressCode 地址
     * @param start       订单状态开始
     * @param end         订单状态结束
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @return 订单列表
     */
    @Transactional(readOnly = true)
    public List<OrderHead> findListByAddressCodeAndStatusRangeAndTimeRange(Long addressCode, int start, int end, int startTime, int endTime) {
        return this.orderHeadDao.findListByAddressCodeAndStatusRangeAndTimeRange(addressCode, start, end, startTime, endTime);
    }

    @Transactional(readOnly = true)
    public List<OrderHead> findListByStatusRangeAndTimeRange(int start, int end, int startTime) {
        return this.orderHeadDao.findListByStatusRangeAndTimeRange(start, end, startTime);
    }
//    /**
//     * 统计客户地址下单数
//     *
//     * @param addressCode
//     * @param startTime
//     * @return
//     */
//    public int countByAddressCode(Long addressCode, int startTime) {
//        return this.orderHeadDao.countByAddressCode(addressCode, startTime);
//    }

//    /**
//     * 统计商品EA数
//     *
//     * @param orderStatuses
//     * @param skuCode
//     * @return
//     */
//    @Transactional(readOnly = true)
//    public BigDecimal sumSkuQty(Set<Integer> orderStatuses, Long skuCode) {
//        return this.orderDetailDao.sumSkuQty(orderStatuses, skuCode);
//    }

    public Pageable<Map<String, Object>> returnable(Long id) {
        OrderHead order = findById(id, false, true).orElseThrow(BusinessException::orderNotFound);
        OrderShippingHead shipping = shippings.findByOrderId(id, true).orElseThrow(BusinessException::shippingNotFound);
        Optional<OrderSignHead> sign = this.signs.findByOrderId(id);
        List<OrderDetail> details = order.getDetails();

        Set<Integer> weighingGoods = details.stream().filter($ -> $.isWeighingGoods()).map($ -> $.getItemCode()).collect(Collectors.toSet());

        Map<Integer, BigDecimal> total = sign.isPresent()
                ? sign.get().getDetails().stream().collect(Collectors.groupingBy($ -> $.getItemCode(),
                Collectors.reducing(BigDecimal.ZERO, $ -> weighingGoods.contains($.getItemCode()) ? $.getRealQty() : $.getQty(), BigDecimal::add)))
                : shipping.getDetails().stream().collect(toMap($ -> $.getItemCode(), $ -> BigDecimal.ZERO));

        Map<Integer, BigDecimal> applied = returnRequestMapper.totalApplied(shipping.getOrderId()).stream()
                .collect(Collectors.groupingBy($ -> $.getItemCode(),
                        Collectors.reducing(BigDecimal.ZERO, $ -> $.deliveredQuantity(), BigDecimal::add)));

        List<Map<String, Object>> returnableItems = details.stream().map($ -> {
            Optional<BigDecimal> totalQuantity = Optional.ofNullable(total.get($.getItemCode()));
            if (!totalQuantity.isPresent()) {
                return Maps.empty();
            }
            BigDecimal returnableQuantity = totalQuantity.get().subtract(Optional.ofNullable(applied.get($.getItemCode())).orElse(BigDecimal.ZERO));
            boolean isWeighingGoods = weighingGoods.contains($.getItemCode());
            return Maps.of("item_code", $.getItemCode(), "sku_id", $.getGoodsCode(), "sku_name", $.getGoodsName(),
                    "price_in_order", $.getSellingPrice(), "returnable_quantity", returnableQuantity,
                    "quantity_in_order", isWeighingGoods ? multiplyWithScale4($.getGoodsSaleUnit(), $.getQuantity()) : $.getQuantity(),
                    "is_weighing_goods", isWeighingGoods, "sale_unit", $.getGoodsSaleUnit(), "package_code", $.getPackageCode(),
                    "package_name", $.packageName());
        }).filter($ -> !$.isEmpty()).collect(toList());
        return new Pageable<>(returnableItems, 1L, (long) returnableItems.size(), (long) returnableItems.size());
    }

    public Optional<OrderHead> findById(Long id, boolean fillExt, boolean fillDetails) {
        return Optional.ofNullable(findByCode(id, fillExt, fillDetails));
    }

    public Map<String, List<Map<String, Object>>> relations(Long id, Criteria criteria) {
        Map<String, List<Map<String, Object>>> result = new HashMap<>();
        if (required(criteria, "orders-required")) {
            OrderHead order = findById(id, false, false).orElseThrow(BusinessException::orderNotFound);
            result.put("orders", newArrayList(Maps.newHashMap("id", order.getOrderCode(), "status", order.getOrderStatus())));
        }
        if (required(criteria, "shippings-required")) {
            shippings.findByOrderId(id, false).ifPresent($ -> {
                result.put("shippings", newArrayList(Maps.newHashMap("id", $.getShippingOrderId(), "status", $.getStatus())));
            });
        }
        if (required(criteria, "signs-required")) {
            signs.findByOrderId(id, false).ifPresent($ -> {
                result.put("signs", newArrayList(Maps.newHashMap("id", $.getReceiptOrderId(), "status", $.getStatus())));
            });
        }
        if (required(criteria, "return-requests-required")) {
            result.put("return_requests", findReturnRequests(id));
        }
        if (required(criteria, "returns-required")) {
            result.put("returns", findReturns(id));
        }
        if (required(criteria, "return-results-required")) {
            result.put("return_results", findReturnResults(id));
        }
        return result;
    }

    private boolean required(Criteria criteria, String key) {
        String required = criteria.getFirst(key);
        return required == null || "true".equalsIgnoreCase(required);
    }

    public List<Map<String, Object>> findOrders(Long id) {
        OrderHead order = findById(id, false, false).orElseThrow(BusinessException::orderNotFound);
        return newArrayList(Maps.newHashMap("id", order.getOrderCode(), "status", order.getOrderStatus()));
    }

    public List<Map<String, Object>> findShippings(Long id) {
        OrderShippingHeadWithBLOBs shipping = shippings.findById(id, false).orElseThrow(BusinessException::shippingNotFound);
        return newArrayList(Maps.newHashMap("id", shipping.getShippingOrderId(), "status", shipping.getStatus()));
    }

    public List<Map<String, Object>> findSigns(Long id) {
        OrderSignHead sign = signs.findById(id, false).orElseThrow(BusinessException::receiptNotFound);
        return newArrayList(Maps.newHashMap("id", sign.getReceiptOrderId(), "status", sign.getStatus()));
    }

    private List<Map<String, Object>> findReturnRequests(Long id) {
        Criteria criteria = new Criteria();
        criteria.add("order-id", id.toString());
        criteria.add("details-required", "false");
        Pageable<ReturnRequest> result = returnRequests.query(criteria, 1L, 50L);
        return result.getEntries().stream()
                .map($ -> Maps.of("id", $.id(), "status", $.status(), "number", $.number()))
                .collect(toList());
    }

    private List<Map<String, Object>> findReturns(Long id) {
        Criteria criteria = new Criteria();
        criteria.add("order-id", id.toString());
        criteria.add("details-required", "false");
        Pageable<Return> result = returns.query(criteria, 1L, 50L);
        return result.getEntries().stream()
                .map($ -> Maps.of("id", $.id(), "status", $.status(), "number", $.number()))
                .collect(toList());
    }

    private List<Map<String, Object>> findReturnResults(Long id) {
        Criteria criteria = new Criteria();
        criteria.add("order-id", id.toString());
        criteria.add("details-required", "false");
        Pageable<ReturnResult> result = returnResults.query(criteria, 1L, 50L);
        return result.getEntries().stream()
                .map($ -> Maps.of("id", $.id(), "status", $.status(), "number", $.number()))
                .collect(toList());
    }

    public Map<String, Object> findNotSign(Long id) {
        OrderHead order = findById(id, false, true).orElseThrow(BusinessException::orderNotFound);
        List<OrderDetail> detailsOfOrder = order.getDetails();
        Optional<OrderSignHead> sign = signs.findByOrderId(id, true);

        Map<Integer, OrderSignDetail> detailsOfSign = sign.map($ -> $.getDetails().stream().collect(Collectors.toMap(OrderSignDetail::getItemCode, $$ -> $$))).orElseGet(Maps::newHashMap);

        return Maps.newHashMap(
                "order", Maps.newHashMap("id", order.getOrderCode()),
                "sign", sign.map($ -> Maps.newHashMap("id", $.getReceiptOrderId())).orElse(null),
                "not_sign", detailsOfOrder.stream().map($ -> {
                    Optional<OrderSignDetail> detailShouldSign = Optional.ofNullable(detailsOfSign.get($.getItemCode()));
                    BigDecimal quantity = detailShouldSign.isPresent() ? subtractWithScale4($.getQuantity(), detailShouldSign.get().getQty()) : $.getQuantity();
                    return !zero(quantity)
                            ? Maps.newHashMap(
                            "item_code", $.getItemCode(),
                            "price", $.getPrice(),
                            "type", $.getType(),
                            "sale_unit", $.getGoodsSaleUnit(),
                            "is_weighing_goods", $.isWeighingGoods(),
                            "sku_id", $.getGoodsCode(),
                            "sku_name", $.getGoodsName(),
                            "quantity", quantity)
                            : Collections.emptyMap();
                }).filter($ -> !$.isEmpty()).collect(toList()));
    }

    public void refundNotify(Long id, Map<String, Object> request) {
        LOGGER.info("order: {} refund notify with request: {}", id, request);
        OrderHead order = findById(id, false, false).orElseThrow(BusinessException::orderNotFound);
        Long returnId = getLong(request, "relation_id");
        Long refundId = getLong(request, "number");
        if (notifyNotRequired(order, returnId)) {
            return;
        }
        Optional<List<Return>> returns = findReturns(order, returnId);
        LOGGER.info("find returns: {}", returns.orElse(Collections.emptyList()).stream().map($ -> $.id()).collect(toList()));
        if (returns.isPresent()) {
            returns.get().forEach($ -> {
                try {
                    $.refundNotify(request);
                } catch (Exception e) {
                    Exceptions.printStackTraceLineByLine(e);
                }
            });
        } else {
            findReturnRequest(order).orElseThrow(BusinessException::returnNotFound).refundNotify(request);
        }
        tasks.addRepeatedHttpTrigger(refundId, order.getVenderId(), Maps.newHashMap("method", "POST", "url", apportionTarget(refundId), "entity", Collections.emptyMap()));
    }

    private String apportionTarget(Long refundId) {
        return omsHost + omsBasePath + "/refunds/" + refundId + "/apportion";
    }

    private boolean notifyNotRequired(OrderHead order, Long returnId) {
        if (Numbers.greaterThanZero(returnId)) {
            LOGGER.info("return id: {}", returnId);
            return false;
        }
        Pageable<ReturnRequest> result = returnRequests.query(Criteria.of(
                "order_id", order.getOrderCode().toString(),
                "occasion", ReturnRequest.Occasion.BEFORE_SIGN.name(),
                "check_immediately", "all",
                "details-required", "false"
        ), 1L, 1L);
        List<ReturnRequest> entries = result.getEntries();
        LOGGER.info("check return request size: {}", entries.size());
        return entries.isEmpty();
    }

    private Optional<List<Return>> findReturns(OrderHead order, Long returnId) {
        if (Numbers.greaterThanZero(returnId)) {
            return returns.find(returnId).map($ -> Lists.newArrayList($));
        } else {
            Criteria criteria = new Criteria();
            criteria.add("order_id", order.getOrderCode().toString());
            criteria.add("occasion", Return.Occasion.BEFORE_SIGN.name());
            criteria.add("return_required", "all");
            Pageable<Return> query = returns.query(criteria, 1L, 5L);
            List<Return> entries = query.getEntries();
            return entries.isEmpty() ? Optional.empty() : Optional.of(entries);
        }
    }

    private Optional<ReturnRequest> findReturnRequest(OrderHead order) {
        Pageable<ReturnRequest> result = returnRequests.query(Criteria.of(
                "order_id", order.getOrderCode().toString(),
                "occasion", ReturnRequest.Occasion.BEFORE_SIGN.name(),
                "check_immediately", "all"
        ), 1L, 1L);
        List<ReturnRequest> entries = result.getEntries();
        LOGGER.info("find return request size: {}", entries.size());
        return entries.isEmpty() ? Optional.empty() : Optional.of(entries.get(0));
    }
}
